import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.HashSet;
import java.util.Set;

import rmi.RMIServer;
import rmi.RMIServerPhilosophInterface;
import rmi.RMIServerTableMasterInterface;

import log.Logger;
import log.SimpleLogger;


/*
 * Steps:
 * 		- start ApplicationRMI with: java -Djava.rmi.server.hostname=192.168.1.52 ApplicationRMI
 * 		- Replace with the local IP where the RMI service should bind to
 * 		- start ApplicationRMI on all involved machines and select 1 (start RMIServer) from the menu
 * 		- choose one machine as your "controlling machine" and start another instance of ApplicationRMI on this machine
 * 		- from the controlling machine, create the tables on the other machines and then start adding Philosophers
 */

public class ApplicationRMI {
	public static void main(String[] args) throws MalformedURLException,
			RemoteException, NotBoundException, InterruptedException {
		
		
		boolean quit = false;
		String host = "localhost", tmp, tmp2, philName = "Phil", ip = getIP();
		Set<String> nameList = new HashSet<String>();
		Logger logger = new SimpleLogger();
		RMIServerTableMasterInterface tableMaster = null;
		
		while (!quit) {
			printMenu();
			int option = Keyin.inInt("Select option: ");

			switch (option) {
			case 1:
				if(RMIServer.run()) {
					System.out.println("RMIServer started successfully on " + ip);
					System.out.println("");
					System.out.println("");
					quit = true;
				}
				else
					System.out.println("Error: RMIServer on this host already up and running");
				break;
			case 2:
				if(tableMaster == null)
					tableMaster = fetchTableMaster();
				
				System.out.println("Creating Table");
				tmp = Keyin.inString("Hostname (Default: "+host+") : ");
				int seats = Keyin.inInt("Seats : ");
				
				host = checkString(tmp, host);
				
				if(tableMaster.createTable(host, seats, logger))
					System.out.println("Table created successfully.");
				else
					System.out.println("Error: Maybe there is already a table on this host?");
				break;
			case 3:
				System.out.println("Creating Philosopher");
				tmp = Keyin.inString("Hostname (Default: "+host+") : ");
				tmp2 = Keyin.inString("Name :");
				int thinkTime = Keyin.inInt("Think Time in s: ") * 1000;
				int eatTime = Keyin.inInt("Eat Time in s: ") * 1000;
				
				host = checkString(tmp, host);
				
				if(tmp2.equals("")) {
					System.out.println("Error: Try again and enter a valid Name.");
					break;
				}
				else {
					if(nameList.contains(tmp2)) {
						System.out.println("Error: Name exists already. Choose different Name.");
						break;
					}
					nameList.add(tmp2);
					philName = tmp2;
				}
				
				RMIServerPhilosophInterface philServer = (RMIServerPhilosophInterface) Naming.lookup("//"+host+"/Philosoph");
				philServer.createAndStart(logger, philName, thinkTime, eatTime, ip);
				System.out.println("Philosopher created successfully.");
				
				break;

			case 4:
				System.out.println("Kill all Philosophers");
				tmp = Keyin.inString("Hostname (Default: "+host+") : ");
				
				host = checkString(tmp, host);
				
				RMIServerPhilosophInterface philServer2 = (RMIServerPhilosophInterface) Naming.lookup("//"+host+"/Philosoph");
				philServer2.stopAll();
				System.out.println("All Philosophers assassinated successfully.");
				break;

			case 99:
				System.out.println("exit");
				quit = true;
				break;
			default:
				System.out.println("Invalid selection.");
			}
		}

	}

	private static void printMenu() {
		System.out.println();
		System.out.println("########################################");
		System.out.println("Please choose:");
		System.out.println("\t1: start RMIServer on this host");
		System.out.println("\t2: create a Table");
		System.out.println("\t3: create a Philosopher");
		System.out.println("\t4: kill all Philosophers");

		System.out.println("\t99: exit");
		System.out.println("########################################");
		System.out.println();
	}
	
	private static RMIServerTableMasterInterface fetchTableMaster() throws MalformedURLException, RemoteException, NotBoundException {
		return (RMIServerTableMasterInterface) Naming.lookup("//localhost/TableMaster");
	}
	
	private static String checkString(String str, String def) {
		if(str.equals(""))
			return def;
		else
			return str;
	}
	
	private static String getIP() {
		try {
			return InetAddress.getLocalHost().getHostAddress();
		}
		catch(UnknownHostException e) {
			return "error";
		}
	}

}

class Keyin {

	// *******************************
	// support methods
	// *******************************
	// Method to display the user's prompt string
	public static void printPrompt(String prompt) {
		System.out.print(prompt + " ");
		System.out.flush();
	}

	// Method to make sure no data is available in the
	// input stream
	public static void inputFlush() {
		@SuppressWarnings("unused")
		int dummy;
		@SuppressWarnings("unused")
		int bAvail;

		try {
			while ((System.in.available()) != 0)
				dummy = System.in.read();
		} catch (java.io.IOException e) {
			System.out.println("Input error");
		}
	}

	// ********************************
	// data input methods for
	// string, int, char, and double
	// ********************************
	public static String inString(String prompt) {
		inputFlush();
		printPrompt(prompt);
		return inString();
	}

	public static String inString() {
		int aChar;
		String s = "";
		boolean finished = false;

		while (!finished) {
			try {
				aChar = System.in.read();
				if (aChar < 0 || (char) aChar == '\n')
					finished = true;
				else if ((char) aChar != '\r')
					s = s + (char) aChar; // Enter into string
			}

			catch (java.io.IOException e) {
				System.out.println("Input error");
				finished = true;
			}
		}
		return s;
	}

	public static int inInt(String prompt) {
		while (true) {
			inputFlush();
			printPrompt(prompt);
			try {
				return Integer.valueOf(inString().trim()).intValue();
			}

			catch (NumberFormatException e) {
				System.out.println("Invalid input. Not an integer");
			}
		}
	}

	public static char inChar(String prompt) {
		int aChar = 0;

		inputFlush();
		printPrompt(prompt);

		try {
			aChar = System.in.read();
		}

		catch (java.io.IOException e) {
			System.out.println("Input error");
		}
		inputFlush();
		return (char) aChar;
	}

	public static double inDouble(String prompt) {
		while (true) {
			inputFlush();
			printPrompt(prompt);
			try {
				return Double.valueOf(inString().trim()).doubleValue();
			}

			catch (NumberFormatException e) {
				System.out
						.println("Invalid input. Not a floating point number");
			}
		}
	}
}
